1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use crate::sketchbook::ids::{UninterpretedFnId, VarId};
use crate::sketchbook::model::{FnTree, ModelState};
use biodivine_lib_param_bn::{BooleanNetwork, FnUpdate};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fmt::{Display, Formatter};

/// Update function of a `BooleanNetwork`.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct UpdateFn {
    expression: String,
    tree: Option<FnTree>,
}

impl Display for UpdateFn {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.expression)
    }
}

impl Default for UpdateFn {
    /// Default "empty" update function.
    fn default() -> UpdateFn {
        UpdateFn {
            expression: String::new(),
            tree: None,
        }
    }
}

impl UpdateFn {
    /// Create new `UpdateFn` from a provided expression.
    ///
    /// The expression is either a valid update fn expression or an empty (possible whitespace) string.
    pub fn try_from_str(expression: &str, context: &ModelState) -> Result<UpdateFn, String> {
        if expression.chars().all(|c| c.is_whitespace()) {
            Ok(UpdateFn::default())
        } else {
            let syntactic_tree = FnTree::try_from_str(expression, context, None)?;
            Ok(UpdateFn {
                expression: syntactic_tree.to_string(context, None),
                tree: Some(syntactic_tree),
            })
        }
    }

    /// Make an "empty" update function (same as [Self::default]).
    pub fn new_empty() -> UpdateFn {
        Self::default()
    }

    /// Get function's expression.
    pub fn get_fn_expression(&self) -> &str {
        &self.expression
    }

    /// Check if the update function is empty (fully unspecified).
    pub fn is_unspecified(&self) -> bool {
        self.tree.is_none()
    }

    /// Set the update function's expression to a given string.
    pub fn set_fn_expression(
        &mut self,
        new_expression: &str,
        context: &ModelState,
    ) -> Result<(), String> {
        if new_expression.chars().all(|c| c.is_whitespace()) {
            self.tree = None;
            self.expression = String::new()
        } else {
            let syntactic_tree = FnTree::try_from_str(new_expression, context, None)?;
            self.expression = syntactic_tree.to_string(context, None);
            self.tree = Some(syntactic_tree);
        }
        Ok(())
    }

    /// Return a set of all variables that are actually used as inputs in this function.
    pub fn to_fn_update(&self, context: &BooleanNetwork) -> Option<FnUpdate> {
        self.tree
            .as_ref()
            .map(|tree| tree.to_fn_update_recursive(context))
    }

    /// Return a set of all variables that are actually used as inputs in this function.
    pub fn collect_variables(&self) -> HashSet<VarId> {
        if let Some(tree) = &self.tree {
            tree.collect_variables()
        } else {
            HashSet::new()
        }
    }

    /// Return a set of all uninterpreted fns that are actually used in this function.
    pub fn collect_fn_symbols(&self) -> HashSet<UninterpretedFnId> {
        if let Some(tree) = &self.tree {
            tree.collect_fn_symbols()
        } else {
            HashSet::new()
        }
    }

    /// Substitute all occurrences of a given function symbol in the syntactic tree.
    pub fn substitute_var(&mut self, old_id: &VarId, new_id: &VarId, context: &ModelState) {
        if let Some(tree) = &self.tree {
            let new_tree = tree.substitute_var(old_id, new_id);
            self.expression = new_tree.to_string(context, None);
            self.tree = Some(new_tree);
        }
    }

    /// Substitute all occurrences of a given function symbol in the syntactic tree.
    pub fn substitute_fn_symbol(
        &mut self,
        old_id: &UninterpretedFnId,
        new_id: &UninterpretedFnId,
        context: &ModelState,
    ) {
        if let Some(tree) = &self.tree {
            let new_tree = tree.substitute_fn_symbol(old_id, new_id);
            self.expression = new_tree.to_string(context, None);
            self.tree = Some(new_tree);
        }
    }

    /// Create update function from another one, substituting all occurrences of a given
    /// function symbol in the syntactic tree. The provided original function object is consumed.
    pub fn with_substituted_fn_symbol(
        mut original_fn: UpdateFn,
        old_id: &UninterpretedFnId,
        new_id: &UninterpretedFnId,
        context: &ModelState,
    ) -> UpdateFn {
        original_fn.substitute_fn_symbol(old_id, new_id, context);
        original_fn
    }

    /// Create update function from another one, substituting all occurrences of a given
    /// variable in the syntactic tree. The provided original function object is consumed.
    pub fn with_substituted_var(
        mut original_fn: UpdateFn,
        old_id: &VarId,
        new_id: &VarId,
        context: &ModelState,
    ) -> UpdateFn {
        original_fn.substitute_var(old_id, new_id, context);
        original_fn
    }
}